from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtWidgets import *
from ultralytics import YOLO
from CameraOperation import *
from MvImport.MvErrorDefine_const import *
from PyUICBasicDemo import Ui_MainWindow

# 全局变量
SN = ""
obj_cam_operation = CameraOperation("")
isOpen = False
isGrabbing = False
yolo = YOLO("yolov8n.pt", task="detection")


# 获取选取设备信息的索引，通过[]之间的字符去解析
def TxtWrapBy(start_str, end, all):
    start = all.find(start_str)
    if start >= 0:
        start += len(start_str)
        end = all.find(end, start)
        if end >= 0:
            return all[start:end].strip()


# 将返回的错误码转换为十六进制显示
def ToHexStr(num):
    chaDic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'}
    hexStr = ""
    if num < 0:
        num = num + 2 ** 32
    while num >= 16:
        digit = num % 16
        hexStr = chaDic.get(digit, str(digit)) + hexStr
        num //= 16
    hexStr = chaDic.get(num, str(num)) + hexStr
    return hexStr

    # 绑定下拉列表至设备信息索引


# ch:枚举相机 | en:enum devices
def enum_devices():
    CameraOperation.enum_devices()
    if (CameraOperation.devList is None) or len(CameraOperation.devList) == 0:
        QMessageBox.warning(ui, "Info", "Find no device", QMessageBox.Ok)
    ui.ComboDevices.clear()
    ui.ComboDevices.addItems(CameraOperation.devList)
    ui.ComboDevices.setCurrentIndex(0)


# ch:打开相机 | en:open device
def open_device():
    global obj_cam_operation, SN, isOpen
    if isOpen:
        QMessageBox.warning(ui, "Error", 'Camera is Running!', QMessageBox.Ok)
        return MV_E_CALLORDER
    SN = ui.ComboDevices.currentText()
    if SN == "":
        QMessageBox.warning(ui, "Error", 'Please select a camera!', QMessageBox.Ok)
        return MV_E_CALLORDER

    obj_cam_operation = CameraOperation(SN)
    ret = obj_cam_operation.Open_device()
    if ret != 0:
        strError = "Open device failed ret:" + ToHexStr(ret)
        QMessageBox.warning(ui, "Error", strError, QMessageBox.Ok)
        isOpen = False
    else:
        set_continue_mode()
        get_param()
        isOpen = True
        enable_controls()


# ch:开始取流 | en:Start grab image
def start_grabbing():
    global obj_cam_operation
    global isGrabbing
    if obj_cam_operation is None:
        return
    ret = obj_cam_operation.Start_grabbing()
    if ret != 0:
        strError = "Start grabbing failed ret:" + ToHexStr(ret)
        QMessageBox.warning(ui, "Error", strError, QMessageBox.Ok)
    else:
        isGrabbing = True
        enable_controls()


# ch:停止取流 | en:Stop grab image
def stop_grabbing():
    global obj_cam_operation
    global isGrabbing
    ret = obj_cam_operation.Stop_grabbing()
    if ret != 0:
        strError = "Stop grabbing failed ret:" + ToHexStr(ret)
        QMessageBox.warning(ui, "Error", strError, QMessageBox.Ok)
    else:
        isGrabbing = False
        enable_controls()


# ch:关闭设备 | Close device
def close_device():
    global isOpen
    global isGrabbing
    global obj_cam_operation

    if isOpen:
        obj_cam_operation.Close_device()
        isOpen = False
    isGrabbing = False
    enable_controls()


# ch:设置触发模式 | en:set trigger mode
def set_continue_mode():
    strError = None
    ret = obj_cam_operation.Set_trigger_mode(False)
    if ret != 0:
        is_trigger_mode = False
        strError = "Set continue mode failed ret:" + ToHexStr(ret) + " mode is " + str(is_trigger_mode)
        QMessageBox.warning(ui, "Error", strError, QMessageBox.Ok)
    else:
        ui.radioContinueMode.setChecked(True)
        ui.radioTriggerMode.setChecked(False)
        ui.bnSoftwareTrigger.setEnabled(False)


# ch:设置软触发模式 | en:set software trigger mode
def set_software_trigger_mode():
    ret = obj_cam_operation.Set_trigger_mode(True)
    if ret != 0:
        strError = "Set trigger mode failed ret:" + ToHexStr(ret)
        QMessageBox.warning(ui, "Error", strError, QMessageBox.Ok)
    else:
        ui.radioContinueMode.setChecked(False)
        ui.radioTriggerMode.setChecked(True)
        ui.bnSoftwareTrigger.setEnabled(isGrabbing)


# ch:设置触发命令 | en:set trigger software
def trigger_once():
    result = obj_cam_operation.Trigger_once()
    if result:
        # 以下三行为halcon预处理算子
        ROI_0 = halcon.gen_rectangle1(1313.35, 2093.67, 3053.78, 3776.86)
        ImageReduced = halcon.reduce_domain(obj_cam_operation.image, ROI_0)
        ImageReduced = halcon.crop_domain(ImageReduced)
        # HObject转npArray
        numArray = HObjectToNpAry(ImageReduced)

        # 传给Yolov8
        results = yolo(source=numArray, show=False)
        # 获取结果并打印在pyqt5控件上
        annotated_frame = results[0].plot()
        #  channel=3 说明返回的是3通道图像
        height, width, channel = annotated_frame.shape
        QImg = QImage(annotated_frame.data, width, height, 3 * width, QImage.Format_RGB888)
        ui.DisImage.set_image(QPixmap.fromImage(QImg))
        ui.DisImage.setScaledContents(True)

        # qt_img = QImage(im, im.shape[1], im.shape[0], im.shape[1] * 3, QImage.Format_RGB888)  # 彩色
        # qt_img = QImage(im, im.shape[1], im.shape[0], im.strides[0], QImage.Format_Grayscale8)  # 黑白
        # ui.DisImage.setPixmap(QPixmap.fromImage(qt_img))


def HObjectToNpAry(image):
    """
    将HObject转成Yolov8需要的类型ndarray
    """
    size = halcon.get_image_size(image)
    im = halcon.himage_as_numpy_array(image)
    data_mono_arr = im.reshape(size[1][0], size[0][0])
    numArray = np.zeros([size[1][0], size[0][0], 3], "uint8")
    numArray[:, :, 0] = data_mono_arr
    numArray[:, :, 1] = data_mono_arr
    numArray[:, :, 2] = data_mono_arr
    return numArray


# ch:存图 | en:save image
def save_bmp():
    ret = obj_cam_operation.Save_Bmp()
    if ret != MV_OK:
        strError = "Save BMP failed ret:" + ToHexStr(ret)
        QMessageBox.warning(ui, "Error", strError, QMessageBox.Ok)
    else:
        print("Save image success")


# ch: 获取参数 | en:get param
def get_param():
    ret = obj_cam_operation.Get_parameter()
    if ret != MV_OK:
        strError = "Get param failed ret:" + ToHexStr(ret)
        QMessageBox.warning(ui, "Error", strError, QMessageBox.Ok)
    else:
        ui.edtExposureTime.setText("{0:.2f}".format(obj_cam_operation.exposure_time))
        ui.edtGain.setText("{0:.2f}".format(obj_cam_operation.gain))
        ui.edtFrameRate.setText("{0:.2f}".format(obj_cam_operation.frame_rate))


# ch: 设置参数 | en:set param
def set_param():
    frame_rate = ui.edtFrameRate.text()
    exposure = ui.edtExposureTime.text()
    gain = ui.edtGain.text()
    ret = obj_cam_operation.Set_parameter(frame_rate, exposure, gain)
    if ret != MV_OK:
        strError = "Set param failed ret:" + ToHexStr(ret)
        QMessageBox.warning(ui, "Error", strError, QMessageBox.Ok)
    return MV_OK


# ch: 设置控件状态 | en:set enable status
def enable_controls():
    global isGrabbing
    global isOpen

    # 先设置group的状态，再单独设置各控件状态
    ui.groupGrab.setEnabled(isOpen)
    ui.groupParam.setEnabled(isOpen)

    ui.bnOpen.setEnabled(not isOpen)
    ui.bnClose.setEnabled(isOpen)

    ui.bnStart.setEnabled(isOpen and (not isGrabbing))
    ui.bnStop.setEnabled(isOpen and isGrabbing)
    ui.bnSoftwareTrigger.setEnabled(isGrabbing and ui.radioTriggerMode.isChecked())

    ui.bnSaveImage.setEnabled(isOpen and isGrabbing)


if __name__ == "__main__":
    # ch: 初始化app, 绑定控件与函数 | en: Init app, bind ui and api
    app = QApplication(sys.argv)

    mainWindow = QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(mainWindow)

    ui.bnEnum.clicked.connect(enum_devices)
    ui.bnOpen.clicked.connect(open_device)
    ui.bnClose.clicked.connect(close_device)
    ui.bnStart.clicked.connect(start_grabbing)
    ui.bnStop.clicked.connect(stop_grabbing)

    ui.bnSoftwareTrigger.clicked.connect(trigger_once)
    ui.radioTriggerMode.clicked.connect(set_software_trigger_mode)
    ui.radioContinueMode.clicked.connect(set_continue_mode)

    ui.bnGetParam.clicked.connect(get_param)
    ui.bnSetParam.clicked.connect(set_param)

    ui.bnSaveImage.clicked.connect(save_bmp)
    mainWindow.show()
    app.exec_()
    close_device()
    sys.exit()
